home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-08-31 | 7.4 KB | 168 lines | [TEXT/CCL ] |
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Copyright 1987, 1988, 1989, 1990 by Ruben Kleiman for Apple Computer, Inc.
- ;;; Advanced Technology Group
-
- ;;;
- ;;; Consult SAMPLE-SESSION.TXT file to see a live demo of how this works.
- ;;;
- ;;; Generic but simple network server for Allegro
- ;;;
-
- (require :event)
-
- (in-package :network :use '(lisp system ccl :event))
-
- (export '(turn-server-on turn-server-off
- turn-client-on turn-client-off
- get-new-client define-server-medium))
-
- ;;; Various supported network media are serviced through the generic
- ;;; functions defined in this file. The lower-level protocols must simply
- ;;; be compatible with these calls.
-
- ;;; A server is defined as a class (currently as an object lisp class object).
- ;;; Servers must support the following messages:
- ;;; oneOf ::= used to create a server instance with arbitrary arguments
- ;;; stream-open ::= initializes a server' storage and creates a logical connection (e.g., a socket)
- ;;; server-on ::= makes the server active (e.g., by registering it)
- ;;; server-off ::= makes the server inactive
- ;;; stream-close ::= terminates a server's storage and logical connection
- ;;; service ::= an efficient message which checks whether a connection request has been
- ;;; received by the server and if so takes the appropiate action
-
- (defobject *server* nil)
-
- (defobfun (exist *server*) (init-list)
- (usual-exist)
- (have 'name (getf init-list :name "unknown")) ; server name
- (have 'type (getf init-list :type "unknown")) ; server type
- (have 'registered-p nil) ; servers are registered by name and type
- (have 'deny-connection! (getf init-list :deny-connection! nil))) ; deny all connection requests
-
- ;;; The *servers* hash table contains an entry for each server keyed on the server medium
- ;;; (e.g., ADSP, ATP, TCP), server name (e.g., "Mariani 2 1st fl") and type (e.g., "laser printer").
- ;;; The entry is the server instance object
- (defvar *servers* (make-hash-table :test #'equal))
-
- ;;; This is an alist whose elements' car is a medium name and cadr is the medium server object class
- ;;; (e.g., for the ADSP medium: (:ADSP *adsp-server*))
- (defvar *supported-server-media* nil)
-
-
- ;;; Holds clients newly opened by servers; entries are stream objects whose class is
- ;;; a particular medium or protocol. New streams may be pushed into *new-clients*
- ;;; by the service message of each server.
- (defvar *new-clients* nil)
-
- (defun make-server (medium &rest init-list &aux server-medium)
- "This is used to create a server object for the appropiate medium"
- (cond ((setq server-medium (assoc medium *supported-server-media*))
- (apply #'oneOf `(,(cadr server-medium) ,@init-list)))
- ((cerror "IGNORE ERROR & CONTINUE..." "Medium ~a not supported" medium))))
-
- (defun get-server (medium &key name type)
- (gethash (read-from-string
- (concatenate 'simple-string (symbol-name medium) name type))
- *servers*))
-
- (defun remove-server (medium &key name type)
- (remhash (read-from-string
- (concatenate 'simple-string (symbol-name medium) name type))
- *servers*))
-
- (defun number-of-servers ()
- (hash-table-count *servers*))
-
- (defun add-server (server medium &key name type)
- (setf (gethash (read-from-string
- (concatenate 'simple-string (symbol-name medium) ; consing doesn't work yet...
- name
- type))
- *servers*)
- server))
-
- (defun turn-server-on (medium &rest service-attributes &aux server)
- (setq server (apply #'make-server `(,medium ,@service-attributes)))
- (ask server (stream-open))
- (ask server (server-on))
- (unless (is-eventhook '(check-servers))
- (add-eventhook '(check-servers) :fast))
- (add-server server medium
- :name (getf service-attributes :name "unknown")
- :type (getf service-attributes :type "unknown"))
- server)
-
- (defun turn-server-off (medium &key name type &aux server)
- (setq server
- (get-server medium :name name :type type))
- (cond (server
- (ask server (server-off))
- (ask server (stream-close))
- (remove-server medium :name name :type type)
- (if (= 0 (number-of-servers))
- (remove-eventhook '(check-servers) :fast)))
- (t
- (cerror "IGNORE ERROR & CONTINUE..." "~a Server ~a not found" type name)))
- server)
-
- ;;; A more efficient version will be needed when many servers co-exist
- ;;; This gets called when each system event is processed
- (defun check-servers ()
- (maphash #'(lambda (key server)
- (ask server (service))) ; service must be F A S T
- *servers*)
- nil) ; MUST return nil
-
- ;;; See ADSP.LISP for the definition of the ADSP driver server
- (defmacro define-server-medium (medium (&body server-request-body) (&body server-error-body))
- (unless (cadr (assoc medium *supported-server-media*))
- (error "Medium ~a is unknown." medium))
- (unless (string-equal (symbol-name (car server-request-body))
- "ON-CLIENT-REQUEST")
- (error "First form must be an ON-CLIENT-REQUEST body."))
- (unless (string-equal (symbol-name (car server-error-body))
- "ON-SERVER-ERROR")
- (error "Second form must be a ON-SERVER-ERROR body."))
- (unless (and (listp (cadr server-error-body))
- (symbolp (caadr server-error-body)))
- (error "ON-SERVER-ERROR's second form is reserved for the error code variable"))
- `(defobfun (service (cadr (assoc ,medium *supported-server-media*))) ()
- (cond ((= (%get-signed-word driver-pb $ioResult) 1)) ; still listening [frequent case] -- avoids a check
- ((= (%get-signed-word driver-pb $ioResult) 0) ; got request [rare]
- (let ((the-client ,@(cdr server-request-body)))
- (if (string-equal "STREAM"
- (symbol-name (type-of the-client)))
- (push the-client *new-clients*)))
- (server-listen))
- (t ; unsuccessful completion
- (let ((,(caadr server-error-body) (%get-signed-word driver-pb $ioResult)))
- ,@(cddr server-error-body))
- (server-listen)))))
-
- (defun get-new-client (&key server-name server-type)
- "Returns a client which requested service type from server name"
- (declare (object-variable service-name service-type))
- (dolist (client *new-clients*)
- (when (and (string= (ask client service-name) server-name)
- (string= (ask client service-type) server-type)
- (multiple-value-bind (state ignore1 ignore2 ignore3 ignore4)
- (ask client (status))
- (eq state 'OPEN)))
- (setq *new-clients* (delete client *new-clients*))
- (return client))))
-
- (defmacro turn-client-on (client-var medium &key server-name server-type)
- (let ((stream-class (read-from-string (format nil "network::*~a-stream*"
- (symbol-name medium)))))
- (unless (and (boundp stream-class)
- (string-equal "STREAM"
- (symbol-name (type-of (eval stream-class)))))
- (error "Stream class for medium ~a is not defined." medium))
- `(progn (setq ,client-var (oneof ,stream-class))
- (ask ,client-var (stream-open ,server-name ,server-type)))))
-
- (defmacro turn-client-off (client-var)
- `(ask ,client-var (stream-close)))
-
- (push :SERVER *features*)
- (provide :server)